// --------------------------------------------------------------------------------------------------------------------
// <copyright file="WindowsAzureTraceFactory.cs" company="AzureContrib (Open source library http://azurecontrib.codeplex.com)">
//   Microsoft Public License (Ms-PL)
// </copyright>
// <summary>
//   Exception for errors when handling Windows Azure Cloud Drives.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;

using AzureContrib.WindowsAzure.Diagnostics.Properties;
using AzureContrib.WindowsAzure.ServiceRuntime;

namespace AzureContrib.WindowsAzure.Diagnostics
{
	/// <summary>
	/// An implementation of <see cref="ITraceFactory"/> targeting Windows Azure.
	/// </summary>
	/// <remarks>Will use [Export(typeof(ITraceFactory))].</remarks>
	public sealed class WindowsAzureTraceFactory : ITraceFactory, IDisposable
	{
		private ITraceSwitchProvider traceSwitchProvider;
		private ITrace internalTrace;

		private bool disposed;

		/// <summary>
		/// Initializes a new instance of the <see cref="WindowsAzureTraceFactory"/> class. 
		/// </summary>
		/// <param name="roleEnvironment">
		/// The role Environment.
		/// </param>
		/// <remarks>
		/// Default trace level is <see cref="LogLevel.Warning"/>.
		/// </remarks>
		/// <remarks>This is the [ImportingConstructor].</remarks>
		public WindowsAzureTraceFactory(IRoleEnvironment roleEnvironment)
			: this(roleEnvironment, null, LogLevel.Warning)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="WindowsAzureTraceFactory"/> class. 
		/// </summary>
		/// <param name="roleEnvironment">
		/// The role Environment.
		/// </param>
		/// <param name="level">
		/// The default Trace Level.
		/// </param>
		public WindowsAzureTraceFactory(IRoleEnvironment roleEnvironment, LogLevel level)
			: this(roleEnvironment, null, level)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="WindowsAzureTraceFactory"/> class. 
		/// </summary>
		/// <param name="roleEnvironment">
		/// The role Environment.
		/// </param>
		/// <param name="traceSwitchProvider">
		/// The control that performs checks on what to log.
		/// </param>
		internal WindowsAzureTraceFactory(IRoleEnvironment roleEnvironment, ITraceSwitchProvider traceSwitchProvider)
			: this(roleEnvironment,  traceSwitchProvider, LogLevel.Warning)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="WindowsAzureTraceFactory"/> class. 
		/// </summary>
		/// <param name="roleEnvironment">
		/// The role Environment.
		/// </param>
		/// <param name="traceSwitchProvider">
		/// The control that performs checks on what to log.
		/// </param>
		/// <param name="defaultLogLevel">
		/// Default trace level is <see cref="LogLevel.Warning"/>
		/// </param>
		[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Dispose is called on this instance but is done via a variable of a diffrent type.")]
		internal WindowsAzureTraceFactory(IRoleEnvironment roleEnvironment, ITraceSwitchProvider traceSwitchProvider, LogLevel defaultLogLevel)
		{
			if (traceSwitchProvider == null)
			{
				traceSwitchProvider = new WindowsAzureTraceSwitchProvider(roleEnvironment, defaultLogLevel);
			}

			this.traceSwitchProvider = traceSwitchProvider;

			internalTrace = DoCreate(GetType(), this.traceSwitchProvider);

			internalTrace.Verbose(string.Format(CultureInfo.CurrentCulture, Strings.CreatedInternalTraceForType, GetType().FullName));

			CreateTraceForType = DoCreate;
			CreateTraceForSwitch = DoCreate;
		}

		/// <summary>
		/// Finalizes an instance of the <see cref="WindowsAzureTraceFactory"/> class.
		/// </summary>
		~WindowsAzureTraceFactory()
		{
			Dispose(false);
		}

		internal Func<Type, ITraceSwitchProvider, ITrace> CreateTraceForType { get; set; }

		internal Func<string, ITraceSwitchProvider, ITrace> CreateTraceForSwitch { get; set; }

		/// <summary>
		/// Create a fully loaded instance of an <see cref="ITrace"/>.
		/// </summary>
		/// <param name="toLogFor">The type to create the trace for.</param>
		/// <returns>The <see cref="ITrace"/> instance.</returns>
		public ITrace Create(Type toLogFor)
		{
			if (toLogFor == null)
			{
				throw new ArgumentNullException("toLogFor");
			}

			var trace = CreateTraceForType(toLogFor, traceSwitchProvider);

			internalTrace.Verbose(Strings.CreatedTracerForType, toLogFor.FullName);

			return trace;
		}

		/// <summary>
		/// Create a fully loaded instance of an <see cref="ITrace"/>.
		/// </summary>
		/// <returns>The <see cref="ITrace"/> instance.</returns>
		public ITrace Create()
		{
			return Create((string)null);
		}

		/// <summary>
		/// Create a fully loaded instance of an <see cref="ITrace"/>.
		/// </summary>
		/// <param name="switch">The name of the switch this trace is for.</param>
		/// <returns>The <see cref="ITrace"/> instance.</returns>
		public ITrace Create(string @switch)
		{
			if (@switch == null)
			{
				throw new ArgumentNullException("switch");
			}

			@switch = @switch.Trim();

			if (string.IsNullOrEmpty(@switch))
			{
				throw new ArgumentOutOfRangeException("switch");
			}

			var trace = CreateTraceForSwitch(@switch, traceSwitchProvider);

			internalTrace.Verbose(string.Format(CultureInfo.CurrentCulture, Strings.CreatedTracerForType, @switch));

			return trace;
		}

		/// <summary>
		/// Dispose the traceSwitchProvider.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		private static ITrace DoCreate(Type toLogFor, ITraceSwitchProvider traceSwitchProvider)
		{
			return new Trace(toLogFor, traceSwitchProvider);
		}

		private static ITrace DoCreate(string @switch, ITraceSwitchProvider traceSwitchProvider)
		{
			return new Trace(@switch, traceSwitchProvider);
		}

		/// <summary>
		/// Dispose internal objects.
		/// </summary>
		/// <param name="disposing">Are we calling from .Dispose() or from finalizer?</param>
		private void Dispose(bool disposing)
		{
			if (disposed)
			{
				return;
			}

			if (disposing)
			{
				var disposable = traceSwitchProvider as IDisposable;
				if (disposable != null)
				{
					disposable.Dispose();
				}				
			}

			disposed = true;
		}
	}
}